/* Copyright (c) 2007 Scott Lembcke
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include "chipmunk.h"

cpPoly
*cpPolyAlloc(void)
{
	return calloc(1, sizeof(cpPoly));
}

cpPoly
*cpPolyInit(cpPoly *poly, int numVerts, cpVect *verts, cpVect offset)
{
	poly->numVerts = numVerts;

	poly->verts = calloc(numVerts, sizeof(cpVect));
	poly->tVerts = calloc(numVerts, sizeof(cpVect));
	poly->axes = calloc(numVerts, sizeof(cpPolyAxis));
	poly->tAxes = calloc(numVerts, sizeof(cpPolyAxis));
	
	int i;
	for(i=0; i<numVerts; i++){
		cpVect a = cpvadd(offset, verts[i]);
		cpVect b = cpvadd(offset, verts[(i+1)%numVerts]);
		cpVect n = cpvnormalize(cpvperp(cpvsub(b, a)));

		poly->verts[i] = a;
		poly->axes[i].n = n;
		poly->axes[i].d = cpvdot(n, a);
	}

	return poly;
}

cpPoly
*cpPolyNew(int numVerts, cpVect *verts, cpVect offset)
{
	return cpPolyInit(cpPolyAlloc(), numVerts, verts, offset);
}

void
cpPolyDestroy(cpPoly *poly)
{
	free(poly->verts);
	free(poly->tVerts);
	
	free(poly->axes);
	free(poly->tAxes);
}

void
cpPolyFree(cpPoly *poly)
{
	if(!poly) return;
	cpPolyDestroy(poly);
	free(poly);
}

void
cpPolyTransformVerts(cpPoly *poly, cpVect p, cpVect rot)
{
	cpVect *src = poly->verts;
	cpVect *dst = poly->tVerts;

	int i;
	for(i=0; i<poly->numVerts; i++)
		dst[i] = cpvadd(p, cpvrotate(src[i], rot));
}

void
cpPolyTransformAxes(cpPoly *poly, cpVect p, cpVect rot)
{
	cpPolyAxis *src = poly->axes;
	cpPolyAxis *dst = poly->tAxes;
	
	int i;
	for(i=0; i<poly->numVerts; i++){
		cpVect n = cpvrotate(src[i].n, rot);
		dst[i].n = n;
		dst[i].d = cpvdot(p, n) + src[i].d;
	}
}

cpBB
cpPolyCacheData(cpPoly *poly, cpVect p, cpVect rot)
{
	float l, b, r, t;
	
	cpPolyTransformAxes(poly, p, rot);
	cpPolyTransformVerts(poly, p, rot);
	
	cpVect *verts = poly->tVerts;
	l = r = verts[0].x;
	b = t = verts[0].y;
	
	int i;
	for(i=1; i<poly->numVerts; i++){
		cpVect v = verts[i];
		
		l = fminf(l, v.x);
		r = fmaxf(r, v.x);
		
		b = fminf(b, v.y);
		t = fmaxf(t, v.y);
	}
	
	return cpBBNew(l, b, r, t);
}

float
cpPolyValueOnAxis(const cpPoly *poly, const cpVect n, const float d)
{
	cpVect *verts = poly->tVerts;
	float min = cpvdot(n, verts[0]);

	int i;
	for(i=1; i<poly->numVerts; i++)
		min = fminf(min, cpvdot(n, verts[i]));
	
	return min - d;
}

int
cpPolyContainsVert(cpPoly *poly, cpVect v)
{
	cpPolyAxis *axes = poly->tAxes;
	
	int i;
	for(i=0; i<poly->numVerts; i++){
		float dist = cpvdot(axes[i].n, v) - axes[i].d;
		if(dist > 0.0) return 0;
	}
	
	return 1;
}
